<script setup lang="ts">
import { onMounted, ref, watch } from 'vue'
import { cursorSize, type CursorType, type Point } from './cursor'

const strokeStyle = '#ffffff' // 线条颜色
const size = ref(cursorSize) // 画布大小
const lineWidth = size.value * 0.2 // 线宽
const arrowWidth = size.value * 0.8 // 向上箭头的宽度
const arrowHeight = size.value * 0.4 // 向上箭头的高度
const radius = size.value * 0.35 // 圆的半径

const props = defineProps<{
  type: CursorType
}>()

const emit = defineEmits<{
  (e: 'onCursorChange', base64: string): void
}>()

const $canvas = ref<HTMLCanvasElement>()
let ctx: CanvasRenderingContext2D
onMounted(() => {
  ctx = $canvas.value?.getContext('2d') as CanvasRenderingContext2D
  getBase64Image(props.type)
})

/**
 * 画箭头
 * @param pathArray 构成箭头的几个点的数组
 */
function drawArrow(pathArray: Array<Point>) {
  ctx.save()
  ctx.beginPath()
  pathArray.forEach((point, index) => {
    if (index === 0) {
      ctx.moveTo(point.x, point.y)
    } else {
      ctx.lineTo(point.x, point.y)
    }
  })
  ctx.lineWidth = lineWidth
  ctx.strokeStyle = strokeStyle
  ctx.stroke()
  ctx.restore()
}

/** 画圆 */
function drawCircle() {
  ctx.save()
  ctx.beginPath()
  ctx.arc(size.value / 2, size.value / 2, radius, 0, Math.PI * 2)
  ctx.lineWidth = lineWidth
  ctx.strokeStyle = strokeStyle
  ctx.stroke()
  ctx.restore()
}

/** 清空画布 */
function clearCanvas() {
  ctx.clearRect(0, 0, size.value, size.value)
}

/** 向上箭头 */
function getUpArrowPoints() {
  return [
    {
      x: (size.value - arrowWidth) / 2,
      y: size.value - (size.value - arrowHeight) / 2,
    },
    { x: size.value / 2, y: (size.value - arrowHeight) / 2 },
    {
      x: size.value - (size.value - arrowWidth) / 2,
      y: size.value - (size.value - arrowHeight) / 2,
    },
  ]
}
/** 向下箭头 */
function getDownArrowPoints() {
  return [
    { x: (size.value - arrowWidth) / 2, y: (size.value - arrowHeight) / 2 },
    { x: size.value / 2, y: size.value - (size.value - arrowHeight) / 2 },
    {
      x: size.value - (size.value - arrowWidth) / 2,
      y: (size.value - arrowHeight) / 2,
    },
  ]
}
/** 向左箭头 */
function getLeftArrowPoints() {
  return [
    {
      x: size.value - (size.value - arrowHeight) / 2,
      y: (size.value - arrowWidth) / 2,
    },
    { x: (size.value - arrowHeight) / 2, y: size.value / 2 },
    {
      x: size.value - (size.value - arrowHeight) / 2,
      y: size.value - (size.value - arrowWidth) / 2,
    },
  ]
}
/** 向右箭头 */
function getRightArrowPoints() {
  return [
    { x: (size.value - arrowHeight) / 2, y: (size.value - arrowWidth) / 2 },
    { x: size.value - (size.value - arrowHeight) / 2, y: size.value / 2 },
    {
      x: (size.value - arrowHeight) / 2,
      y: size.value - (size.value - arrowWidth) / 2,
    },
  ]
}
/** 表示不允许的圆圈+斜线 */
function getNoPoints() {
  const hr = size.value / 2 // 一半大小
  const xw = radius * Math.sin(Math.PI / 4) // 水平方向距离中点的长度
  const yw = radius * Math.cos(Math.PI / 4) // 垂直方向距离中点的长度
  return [
    { x: hr - xw, y: hr - yw },
    { x: hr + xw, y: hr + yw },
  ]
}
/** 各个类型对应的直线点的映射 */
const map = {
  up: getUpArrowPoints,
  down: getDownArrowPoints,
  left: getLeftArrowPoints,
  right: getRightArrowPoints,
  no: getNoPoints,
  default: undefined,
}
/**
 * 获取光标类型的base64字符串。默认类型则返回空字符串
 * @param type 光标类型
 */
function getBase64Image(type: CursorType) {
  const f = map[type]
  if (f) {
    const points = f()
    clearCanvas()
    drawArrow(points)
    if (type === 'no') {
      drawCircle()
    }
    return $canvas.value?.toDataURL() as string
  } else {
    return ''
  }
}

// 监听光标类型变化
watch(
  () => props.type,
  () => {
    emitCursorImage()
  }
)
/** 向上传递图形字符串 */
function emitCursorImage() {
  const base64 = getBase64Image(props.type)
  emit('onCursorChange', base64)
}
</script>

<template>
  <canvas ref="$canvas" :width="size" :height="size" class="cursor" />
</template>

<style scoped lang="less">
.cursor {
  position: absolute;
  top: -666;
}
</style>
